package main

import (
	"fmt"
	"reflect"
	// "io"
	// "encoding/json"
	// "strings"
)

// type Value struct{
// 	type *rtype
// 	ptr unsafe.Pointer
// 	flag
// }

func main() {
	// i:=3
	// iv:=reflect.ValueOf(i)
	// it:=reflect.TypeOf(i)
	// fmt.Println(iv,it)

	// reflect.Value和int互转
	// i:=3
	// iv:=reflect.ValueOf(i)
	// i1:=iv.Interface().(int)
	// fmt.Println(i1)

	// 修改
	// i := 3
	// ipv := reflect.ValueOf(&i)
	// ipv.Elem().SetInt(4)
	// fmt.Println(i)

	// 修改struct
	// p:=person{Name:"张三",Age:24}
	// ppv:=reflect.ValueOf(&p)
	// ppv.Elem().Field(0).SetString("李四")
	// fmt.Println(p)

	// 获取底层类型 Kind方法返回一个Kind类型的值
	// p:=person{Name:"张三",Age:26}
	// ppv:=reflect.ValueOf(&p)
	// fmt.Println(ppv.Kind())
	// pv:=reflect.ValueOf(p)
	// fmt.Println(pv.Kind())

	// p:=person{Name:"zzhangsan",Age:25}
	// pt:=reflect.TypeOf(p)
	// /*
	// 	你可以通过 FieldByName 方法获取指定的字段，也可以通过 MethodByName 方法获取指定的方法，这在需要获取某个特定的字段或者方法时非常高效，而不是使用遍历
	// */
	// // 遍历person字段
	// for i := 0; i < pt.NumField(); i++ {
	// 	fmt.Println("字段：",pt.Field(i).Name)
	// }
	// // 遍历person的方法
	// for i := 0; i < pt.NumMethod(); i++ {
	// 	fmt.Println("方法：",pt.Method(i).Name)
	// }

	// 是否实现接口 尽可能通过类型断言的方式判断是否实现了某接口，而不是通过反射。
	// p:=person{Name:"zhagsan",Age:27}
	// pt:=reflect.TypeOf(p)
	// stringerType:=reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
	// writerType:=reflect.TypeOf((*io.Writer)(nil)).Elem()
	// fmt.Println("是否实现了fmt.Stringer",pt.Implements(stringerType))
	// fmt.Println("是否是实现了io.Writer:",pt.Implements(writerType))

	// json和struct互转
	// p:=person{Name:"hehe",Age:29}
	// // struct to json
	// jsonB,err:=json.Marshal(p)
	// if err==nil {
	// 	fmt.Println(string(jsonB))
	// }
	// // json to struct
	// respJSON:="{\"Name\":\"李四\",\"Age\":40}"
	// json.Unmarshal([]byte(respJSON),&p)
	// fmt.Println("json:",p)

	// struct tag 反射获取tag
	// p:=person{Name:"zhagsan",Age:27}
	// pt:=reflect.TypeOf(p)
	// for i := 0; i < pt.NumField(); i++ {
	// 	sf:=pt.Field(i)
	// 	// fmt.Printf("字段%s上,json tag为%s\n",sf.Name,sf.Tag.Get("json"))
	// 	fmt.Printf("字段%s上,json tag为%s\n",sf.Name,sf.Tag.Get("json"))
	// 	fmt.Printf("字段%s上,bson tag为%s\n",sf.Name,sf.Tag.Get("bson"))
	// }

	// struct to json  自己实现转json 字符串拼接
	// p:=person{Name: "飞雪无情",Age: 20}
	// pv:=reflect.ValueOf(p)
	// pt:=reflect.TypeOf(p)
	// jsonBuilder:=strings.Builder{}
	// jsonBuilder.WriteString("{")
	// num:=pt.NumField()
	// for i := 0; i < num; i++ {
	// 	jsonTag:=pt.Field(i).Tag.Get("json") // tag
	// 	jsonBuilder.WriteString("\""+jsonTag+"\"")
	//   	jsonBuilder.WriteString(":")
	// 	// 字段值
	// 	jsonBuilder.WriteString(fmt.Sprintf("\"%v\"",pv.Field(i)))
	// 	if i<num-1{
	// 		jsonBuilder.WriteString(",")
	// 	}
	// }
	// jsonBuilder.WriteString("}")
	// fmt.Println(jsonBuilder.String())

	// demo
	p := person{Name: "飞雪无情", Age: 20}
	pv := reflect.ValueOf(p)
	// 反射调用Print方法
	mPrint := pv.MethodByName("Print")
	args := []reflect.Value{reflect.ValueOf("登录")}
	mPrint.Call(args)

}

/*
	修改值的规则：
	可被寻址，通俗地讲就是要向 reflect.ValueOf 函数传递一个指针作为参数。
	如果要修改 struct 结构体字段值的话，该字段需要是可导出的，而不是私有的，也就是该字段的首字母为大写。
	记得使用 Elem 方法获得指针指向的值，这样才能调用 Set 系列方法进行修改。
*/
type person struct {
	Name string `json:"name" bson:"b_name"` // 键盘数字1前面的符号
	Age  int    `json:"age" bson:"b_age"`
}

/*
	json 作为 Key，是 Go 语言自带的 json 包解析 JSON 的一种约定，
	它会通过 json 这个 Key 找到对应的值，用于 JSON 的 Key 值
*/
func (p person) String() string {
	return fmt.Sprintf("name:%s,Age:%d", p.Name, p.Age)
}

func (p person) Print(prefix string) {
	fmt.Printf("%s:Name is %s,Age is %d\n", prefix, p.Name, p.Age)
}

/*
	反射虽然很强大，可以简化编程、减少重复代码，但是过度使用会让你的代码变得复杂混乱。
	所以除非非常必要，否则尽可能少地使用它们。
*/
